package stc.skymobi.netty.handler.codec.http.transformer;

import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.multipart.Attribute;
import io.netty.handler.codec.http.multipart.FileUpload;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder;
import io.netty.handler.codec.http.multipart.InterfaceHttpData;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import stc.skymobi.bean.kv.KVDecoder;
import stc.skymobi.bean.kv.KVUtils;
import stc.skymobi.bean.kv.annotation.KeyValueAttribute;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hp
 */
public class UrlencodedTransformer implements HttpRequestTransformer {
	private static Logger logger = LoggerFactory
			.getLogger(UrlencodedTransformer.class);

	private KVDecoder decoder;

	/**
	 * @param decoder
	 *            the decoder to set
	 */
	public void setDecoder(KVDecoder decoder) {
		this.decoder = decoder;
	}

	@Override
	public Object transform(FullHttpRequest request) {
		Map<String, FileUpload> fileUploads = null;
		boolean multipart = false;
		StringBuilder uri = new StringBuilder(request.getUri());
		String contentType = request.headers().get(
				HttpHeaders.Names.CONTENT_TYPE);
		if (contentType == null || !contentType.contains("multipart/form-data")) {
			String bodyContent = request.content().toString(CharsetUtil.UTF_8);
			uri.append("&").append(bodyContent);
		} else {
			try {
				HttpPostRequestDecoder postRequestDecoder = new HttpPostRequestDecoder(
						request);
				if (postRequestDecoder.isMultipart()) {
					logger.debug("decoding multipart request...");
					multipart = true;
					fileUploads = new HashMap<String, FileUpload>();
					for (InterfaceHttpData data : postRequestDecoder
							.getBodyHttpDatas()) {
						switch (data.getHttpDataType()) {
						case Attribute:
							Attribute attribute = (Attribute) data;
							uri.append("&").append(attribute.getName())
									.append("=").append(attribute.getValue());
							break;
						case FileUpload:
							FileUpload fileUpload = (FileUpload) data;
							fileUploads.put(fileUpload.getName(), fileUpload);
						}
					}
				}
			} catch (Exception e) {
				logger.error("decode multipart/form-data error:", e);
			}
		}

		QueryStringDecoder queryStringDecoder = new QueryStringDecoder(
				uri.toString());

		Object bean = decoder.transform(queryStringDecoder.parameters());

		if (multipart && fileUploads != null && !fileUploads.isEmpty()) {
			Field[] fields = KVUtils.getKVFieldsOf(bean.getClass());
			for (Field field : fields) {
				KeyValueAttribute param = field
						.getAnnotation(KeyValueAttribute.class);
				if (null == param) {
					// not KVAttribute
					continue;
				}

				FileUpload fileUpload = fileUploads.get(param.key());

				if (null != fileUpload) {
					if (logger.isDebugEnabled()) {
						logger.debug("found file field[" + field + "]");
					}

					field.setAccessible(true);
					Class<?> fieldType = field.getType();
					if (fieldType == FileUpload.class) {
						try {
							field.set(bean, fileUpload);
						} catch (Exception e) {
							logger.error("transform file error:", e);
						}
					} else {
						logger.warn(
								"field {} can't accept FileUpload type,drop the uploaded file {}",
								field, fileUpload.getFilename());
					}
				}
			}
		}
		return bean;
	}

}
